Obsidian EndTime 1.1.3
前のバージョン Obsidian EndTime 1.1.2
次のバージョン Obsidian EndTime 1.1.4
Created: 2024-10-01 16:39
変更点
データ更新の頻度を1分から1秒に変更(1分おきだと、タスクリストや残り時間の更新反映が遅すぎるので)
超過した時の記号が目立たないので変更 (⚠️⇒🚨)
デバッグ用のログをコメントアウト
仕様
1秒ごとにデータ更新
🗂️から始まる行はセクションとして認識
マークダウン形式のURLが含まれる場合、URL部分は表示しないように変更(URLが長すぎて可読性が下がるため)
実行中のタスクは、残り時間を➡️残り10分のように表示。超過している場合は 🚨超過5分 のように表示。
気になる点
タスク編集時のサイドバーへの反映がもったり
code:main.js
const { ItemView, WorkspaceLeaf } = require("obsidian");
const VIEW_TYPE_TASK_SIDEBAR = "task-sidebar-view";
class TaskSidebarView extends ItemView {
constructor(leaf) {
super(leaf);
}
getViewType() {
return VIEW_TYPE_TASK_SIDEBAR;
}
getDisplayText() {
return "Task Sidebar";
}
async onOpen() {
console.log("TaskSidebarView opened.");
const container = this.containerEl.children1; // Safely access the second child of containerEl
if (!container) {
console.error("Container not found in TaskSidebarView.");
return;
}
container.empty();
container.createEl("h4", { text: "Task Schedule" });
}
async updateTasks(taskList) {
// console.log("Updating task list in TaskSidebarView:", taskList);
const container = this.containerEl.children1;
if (!container) {
console.error("Container not found when updating tasks.");
return;
}
container.empty(); // Clear previous task list
taskList.forEach((task) => {
let taskName, timeInfo = task.split('||'); // Split taskName and timeInfo by '||'
// Remove the URL part in Markdown links (e.g., Link Text(http://example.com) => "Link Text")
taskName = taskName.replace(/\[(^\]+)\]\(^\)+\)/g, '$1');
const taskItem = container.createEl("div", { cls: "task-item" });
// Create first line for task name or section
let taskNameEl;
if (taskName.startsWith("🗂️")) {
taskNameEl = taskItem.createEl("p", { text: taskName, cls: "section" }); // Apply 'section' class
} else {
taskNameEl = taskItem.createEl("p", { text: taskName, cls: "task-name" }); // Apply 'task-name' class
}
// Create second line for time info (grayed out and smaller font)
const timeEl = taskItem.createEl("p", { text: timeInfo, cls: "task-time" });
timeEl.style.color = "#888"; // Gray out the text
timeEl.style.fontSize = "0.9em"; // Slightly smaller font size
});
}
async onClose() {
console.log("TaskSidebarView closed.");
}
}
module.exports = class TaskSchedulePlugin extends require("obsidian").Plugin {
async onload() {
console.log("Task Schedule Plugin loaded.");
// Register the sidebar view
this.registerView(VIEW_TYPE_TASK_SIDEBAR, (leaf) => new TaskSidebarView(leaf));
// Add a command to display the task info in the sidebar
this.addCommand({
id: "show-tasks-sidebar",
name: "Show Tasks in Sidebar",
callback: () => this.showTasksSidebar(),
});
// Open the view when the plugin is loaded
this.activateView();
// Update tasks every 1 second (1000 milliseconds)
this.updateInterval = setInterval(() => {
this.showTasksSidebar();
}, 1000); // 1000 milliseconds = 1 second
}
onunload() {
console.log("Task Schedule Plugin unloaded.");
this.app.workspace.detachLeavesOfType(VIEW_TYPE_TASK_SIDEBAR);
// Clear the interval when the plugin is unloaded
clearInterval(this.updateInterval);
}
async activateView() {
console.log("Activating the TaskSidebarView...");
try {
// Check if there's already a leaf for the sidebar
let leaf = this.app.workspace.getLeavesOfType(VIEW_TYPE_TASK_SIDEBAR).first();
if (!leaf) {
console.log("No existing sidebar leaf found, creating a new one.");
// Create a new right leaf if none exists
const rightLeaf = this.app.workspace.getRightLeaf(false);
if (!rightLeaf) {
console.log("No right leaf found, creating a new one by splitting the workspace.");
leaf = this.app.workspace.createLeafBySplit(this.app.workspace.rootSplit, "horizontal", false);
} else {
leaf = rightLeaf;
}
}
// Set the view state to our custom sidebar view
await leaf.setViewState({
type: VIEW_TYPE_TASK_SIDEBAR,
active: true,
});
// Reveal the sidebar leaf
this.app.workspace.revealLeaf(leaf);
console.log("TaskSidebarView activated and revealed.");
} catch (error) {
console.error("Error while activating the TaskSidebarView:", error);
}
}
showTasksSidebar() {
console.log("Displaying tasks in the sidebar.");
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) {
console.warn("No active file found.");
new this.app.Notice("No active file.");
return;
}
// console.log("Reading the active file:", activeFile);
this.app.vault.read(activeFile).then((noteText) => {
// console.log("File content:", noteText);
const tasks = noteText.match(/- \ \(?: (\d{2}:\d{2})-)?(?: \\(\d+))?(.*)|🗂️.*/g);
if (!tasks) {
console.warn("No tasks found in the note.");
new this.app.Notice("No tasks found.");
return;
}
const endTimes = [];
let currentDateTime = new Date();
let lastEndTime = "";
tasks.forEach((task, index) => {
// console.log("Processing task:", task);
if (task.startsWith("🗂️")) {
endTimes.push(task);
return;
}
const match = task.match(/- \ \(?: (\d{2}:\d{2})-)?(?: \\(\d+))?(.*)/);
if (!match) {
console.warn("Task did not match the expected format:", task);
return;
}
const startTime = match1;
const duration = match2 ? parseInt(match2, 10) : 0;
const taskName = match3.trim();
let baseTime;
let actualStartTime;
if (index === 0 || !endTimesendTimes.length - 1.startsWith("終了予定時刻")) {
if (startTime) {
const hours, minutes = startTime.split(':').map(Number);
baseTime = new Date();
baseTime.setHours(hours, minutes, 0, 0);
// console.log("Task start time set to:", baseTime);
} else {
baseTime = new Date(currentDateTime);
// console.log("Task start time set to current time:", baseTime);
}
} else {
baseTime = new Date(currentDateTime);
// console.log("Task base time set to:", baseTime);
}
actualStartTime = new Date(baseTime);
const endDate = new Date(baseTime.getTime() + duration * 60000);
if (endDate < new Date()) {
endDate.setTime(Date.now());
}
const startTimeStr = actualStartTime.toTimeString().slice(0, 5);
const endTimeStr = endDate.toTimeString().slice(0, 5);
lastEndTime = endTimeStr;
let taskOutput = ${taskName}||${startTimeStr} - ${endTimeStr} (${duration}分);
// console.log("Formatted task output:", taskOutput);
// Only add remaining time if the start time is set
if (startTime) {
let elapsedTime = Math.ceil((Date.now() - actualStartTime.getTime()) / 60000);
let remainingTimeInfo = '';
if (elapsedTime > duration) {
let overdueMinutes = elapsedTime - duration;
remainingTimeInfo = 🚨超過${overdueMinutes}分;
} else if (duration > 0) {
let remainingTime = duration - elapsedTime;
remainingTimeInfo = ➡️残り${remainingTime}分;
} else {
remainingTimeInfo = '🕒残り時間なし';
}
taskOutput += ${remainingTimeInfo};
}
endTimes.push(taskOutput);
currentDateTime = endDate;
});
// console.log("Final task list:", endTimes);
// Get the custom sidebar view and update tasks
const view = this.app.workspace.getLeavesOfType(VIEW_TYPE_TASK_SIDEBAR)0.view;
if (view instanceof TaskSidebarView) {
// console.log("Updating tasks in the sidebar view.");
view.updateTasks(endTimes);
} else {
console.error("Failed to find or initialize the TaskSidebarView.");
}
}).catch((error) => {
console.error("Error reading the file:", error);
});
}
};
code:styles.css
.task-item {
margin-bottom: 1em; /* Add some space between tasks */
}
.task-name {
margin-bottom: -0.5em; /* Reduce space between task name and time */
line-height: 1; /* Slightly reduce line-height */
}
.task-time {
color: #888; /* Gray out the text */
font-size: 0.9em; /* Slightly smaller font size */
line-height: 1; /* Slightly reduce line-height */
}
/* Section (🗂️) specific styling */
.section {
font-weight: bold; /* Make section titles bold */
margin-bottom: 0.5em; /* Add some space after section titles */
line-height: 1.2; /* Adjust line-height for sections */
}